/* 简介：cothread 是一个轻量级协程调度器，由纯C语言实现，易于移植到各种单片机。
 * 同时，由于该调度器仅仅运行在一个实际线程中，所以它也适用于服务器高并发场景。
 *
 * 版本: 1.0.0   2019/02/25
 *
 * 作者: 覃攀 <qinpan1003@qq.com>
 *
 */

#include <pthread.h>
#include "rtos.h"

int thread_test(int argc,char **argv)
{
    return 0;
}

/* demo 需要提供给 rtos 的接口函数 */
/*-----------------------------------------------------------------------*/
static int io_inited = 0;
static int input_arrived = 0;
static char ch;

static pthread_t thread_hold = 0;
static irq_state_t interrupt_disabled = 0;
static int tx_inprogress = 0;

static char write_buff[1024];
static int write_head = 0;
static int write_tail = 0;

static void uart_irq_handler(void)
{
    if (read_flag())
        wakeup_shell_thread();

    if (write_completed())
        wakeup_log_thread();

    cothread_scheduler_wakeup();
}

static void ll_read(void)
{
    if (input_arrived)
        return;

    input_arrived =  fread(&ch, 1, 1, stdin);
    
    if (input_arrived)
        uart_irq_handler();
}

static void ll_write(void)
{
    int count = 0;
    int write_head_tmp = write_head;
    
    if (write_tail == write_head_tmp)
        return;

    tx_inprogress = 1;

    /* 控制每次发送字节数，模拟串口发送阻塞，波特率 = bytes * 8 * 1000 */
    int bytes = 10;
    while (write_tail != write_head_tmp && count < bytes)
    {
        printf("%c", write_buff[write_tail]);
        fflush(stdout);

        if (++write_tail >= sizeof(write_buff))
            write_tail = 0;

        count++;
    }

    if (write_tail != write_head_tmp)
        return;
    
    tx_inprogress = 0;
    uart_irq_handler();
}

static void io_init(void)
{
    int flags = fcntl(0, F_GETFL, 0);
    fcntl(0, F_SETFL, flags | O_NONBLOCK);

    io_inited = 1;
}

int irq_log_enable = 0;
int enable_irq_log(int argc, char **argv)
{
    if (argc < 2)
        return - 1;

    if (strcmp(argv[1], "on") == 0)
        irq_log_enable = 1;
    else if (strcmp(argv[1], "off") == 0)
        irq_log_enable = 0;

    return 0;
}

static int timer_count = 0;
static void timer_thread(int signo)
{
    signal(SIGALRM, timer_thread);
    
    if (interrupt_disabled)
        return;

    timer_count++;

    /* 模拟串口输入中断 */
    ll_read();
    
    /* 模拟串口发送 */
    ll_write();

    system_tick();

    if (irq_log_enable)
        LOG("log from interrupt.\n");
}

static void hardware_init(void)
{
    io_init();
}

int read_flag(void)
{
    if (!io_inited)
        return 0;

    return input_arrived;
}

int read_data(void)
{
    int ch_tmp = ch;
    
    input_arrived = 0;
    
    return ch_tmp;
}

int write_completed(void)
{
    if (!io_inited)
        return 0;

    return !tx_inprogress;
}

void write_ch(char ch)
{
    int write_head_tmp;

    if (!io_inited)
        return;

    write_head_tmp = write_head + 1;
    if (write_head_tmp >= sizeof(write_buff))
        write_head_tmp = 0;

    /* 满 */
    if (write_tail == write_head_tmp)
        return;

    write_buff[write_head] = ch;
    write_head = write_head_tmp;
}

irq_state_t irq_save_disable(void)
{
    while (interrupt_disabled && thread_hold != pthread_self())
        usleep(2);

    interrupt_disabled = 1;
    thread_hold = pthread_self();
    return 0;
}

void irq_restore(irq_state_t stat)
{
    interrupt_disabled = 0;
}

void system_timer_start(void)
{
    struct itimerval value, ovalue;

    signal(SIGALRM, timer_thread);

    value.it_value.tv_sec = 0;
    value.it_value.tv_usec = 1000;
    value.it_interval.tv_sec = 0;
    value.it_interval.tv_usec = 1000;
    
    setitimer(ITIMER_REAL, &value, &ovalue);
}

static pthread_mutex_t wakeup_mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t wakeup_cond = PTHREAD_COND_INITIALIZER;

void cothread_scheduler_wakeup(void)
{
    if (pthread_mutex_trylock(&wakeup_mtx))
        return;
    
    pthread_cond_broadcast(&wakeup_cond);
    pthread_mutex_unlock(&wakeup_mtx);
}

void cothread_scheduler_wait(void)
{
    if (pthread_mutex_trylock(&wakeup_mtx))
        return;
    
    pthread_cond_wait(&wakeup_cond, &wakeup_mtx);
    pthread_mutex_unlock(&wakeup_mtx);
}

/*-----------------------------------------------------------------------*/
static coresult_t main_thread_8(ccb_t *ccb)
{    
    thread_start();

    while (1)
    {
        thread_sleep(1000);
        LOG("[%s] run on scheduler %p.\n", __FUNCTION__, ccb->scheduler);
    }
    
    thread_end();
}

static coresult_t main_thread_9(ccb_t *ccb)
{    
    thread_start();

    while (1)
    {
        thread_sleep(300);
        LOG("[%s] run on scheduler %p.\n", __FUNCTION__, ccb->scheduler);
    }
    
    thread_end();
}

static void sig_block(int sig)
{
    sigset_t sigset;
    
    sigemptyset(&sigset);
    sigaddset(&sigset, sig);
    pthread_sigmask(SIG_BLOCK, &sigset, NULL);
}

static void *rtos_thread1(void *arg)
{
    sig_block(SIGALRM);

    struct cothread_scheduler *scheduler = 
                alloc_scheduler(COTHREAD_NR, THREAD_PRIO_NR, (void *)pthread_self());
    
    create_shell_thread();
    create_log_thread();

    int i;
    for (i = 0; i < 20; i++)
        thread_create_on_scheduler(scheduler, main_thread_8, 
                NULL, THREAD_PRIO_HIGH);

    while (1)
    {
        if (cothread_loop_once(scheduler) <= 0)
            cothread_scheduler_wait();
    }
    
    return NULL;
}

static void *rtos_thread2(void *arg)
{    
    sig_block(SIGALRM);

    struct cothread_scheduler *scheduler = 
                alloc_scheduler(50, 3, (void *)pthread_self());

    int i;
    for (i = 0; i < 20; i++)
        thread_create_on_scheduler(scheduler, main_thread_9, 
                NULL, THREAD_PRIO_HIGH);

    while (1)
    {
        if (cothread_loop_once(scheduler) <= 0)
            cothread_scheduler_wait();
    }
    
    return NULL;
}

/* 1、使用 pthread 模拟两个 rtos 线程
 * 2、在 rtos 线程中建立 cothread 调度器，通过 cothread 实现多线程
 * 这样可以结合 rtos 编程的便利、cothread 小内存优势
 */
int main(void)
{
    pthread_t pid1, pid2;

    hardware_init();
    system_timer_start();
    
    pthread_create(&pid1, NULL, rtos_thread1, NULL);
    pthread_create(&pid2, NULL, rtos_thread2, NULL);

    pthread_join(pid1, NULL);
    pthread_join(pid2, NULL);

    return 0;
}

